#pragma once

#define LARGE_PAGE_MARKER 0x8000
#define PAGE_SIZE 4096
#define FREE_LIST_SIZE 13
#define BITMAP_SIZE 128
#define BITMAP_FULL_MARKER 0xFF
#define LARGE_PAGE_MASK (0x7FFF)
#define REMOVE_LARGE_PAGE_MARKER(size) (size&0x7FFF)
#define NUM_PAGES_FROM_SIZE(size) ((size+PAGE_SIZE-1)>>12)
#define SIZE_FROM_NUM_PAGES(num_pages) (num_pages*PAGE_SIZE)
#define INDEX2SIZE(index) (1<<index)
#define LISTINDEX(index) ((index<2)?2:index)
#define ROUNDDOWN(addr) (void *)(((int)addr)&(~(PAGE_SIZE-1)))
#define FIRST_DATA_BLOCK(base_ptr, size) (void *)((int)base_ptr+(size-(sizeof(struct page_header)%size))+sizeof(struct page_header))
#define BLOCKNO(ptr,size) (((int)ptr-(int)FIRST_DATA_BLOCK(ROUNDDOWN(ptr),size))/size)
#define BLOCKNO2PTR(base_ptr, num, size) (void *)((int)FIRST_DATA_BLOCK(base_ptr, size) + num*size)
#define NUMBLOCKS(size) ((PAGE_SIZE-(int)(FIRST_DATA_BLOCK(NULL,size)))/size)
#define BLOCKNO2BIT(blockno) (1<<(blockno%8))
#define USED_BITMAP_SIZE(blk_size)  ((NUMBLOCKS(blk_size) + 7)/8)
#define SET_BITMAP(bitmap, blkno)	(bitmap[blkno/8] |= BLOCKNO2BIT(blkno))
#define RESET_BITMAP(bitmap, blkno)	(bitmap[blkno/8] &= ~BLOCKNO2BIT(blkno))


struct free_entry {
	struct page_header *free_start;
	struct page_header *free_end;
};

struct page_header {
  short size;
  short allocated_blocks;
  void *free_ptr;
  char bitmap[BITMAP_SIZE];
};

struct large_page_header {
  short size;
};

void *allocate_new_page(size_t size);
void *allocate_mmap_pages(size_t num_pages);
void *allocate_remap_pages(void *ptr, size_t new_size);
void free_mmap_pages(void *ptr, size_t num_pages);
void init_page(void *page, size_t alloc_size);
void init_large_page(void *page, size_t alloc_size);
